package net.sansi.v3correctserver.demo;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Created by Fang
 * create time : 2025/2/26
 * IDE : IntelliJ IDEA
 * <p>
 * 一个简单的观察者模式实现Demo
 */

public class Observer {

    /**
     * 监听器接口定义
     */
    public interface Listener {
        void invoke(Object... args);
    }

    /**
     * 所有回调函数
     * 1. key 为事件名称（可以是 taskId)
     * 2. value 为与该事件名绑定的回调函数列表，这里的回调函数全部用 Listener 包装
     * 3. 这里 value 使用 set 实际实现如果回调函数不多的话可以使用 List代替
     */
    private Map<Object, Set<Listener>> listeners = new HashMap<>();

    /**
     * 移除某个监听器
     * 1. 从 listeners 中将key 为 event 中的 listener 移除
     * 2. 这里 listener 函数指针必须相同
     *
     * @param event    事件名称
     * @param listener 监听器
     */
    public void removeListener(Object event, Listener listener) {
        if (!listeners.containsKey(event)) return;
        Set<Listener> list = listeners.get(event);
        list.remove(listener);
    }


    /**
     * 移除监听器回调接口
     */
    public interface RemoveListener {
        void remove();
    }

    /**
     * 添加监听器（订阅）
     * 1. 因为移除监听器需要 Listener 指针
     * 2. 但是调用方为了方便起见， Listener 往往使用匿名创建，调用方没有指针；
     * 3. 所以这里订阅成功后，返回一个取消订阅函数，用于方便取消订阅；
     *
     * @param event    时间
     * @param listener 监听器
     * @return 移除该监听器方法
     */
    public RemoveListener addListener(Object event, Listener listener) {
        if (!listeners.containsKey(event)) {
            listeners.put(event, new HashSet<>());
        }
        listeners.get(event).add(listener);
        return () -> removeListener(event, listener);
        // 上面是 Lambda 语法糖， 实际为下面的代码
        //return new RemoveListener() {
        //    @Override
        //    public void remove() {
        //        removeListener(event, listener);
        //    }
        //};
    }

    /**
     * 触发器
     * 1. 找到和event相关的所有 Listener
     * 2. 调用 Listener 下的 invoke 函数执行回调；
     *
     * @param event 需要触发的事件
     * @param args  参数，这些参数将传递给 Listener
     */
    public void trigger(Object event, Object... args) {
        if (!listeners.containsKey(event)) return;
        Set<Listener> list = listeners.get(event);
        for (Listener lsr : list) {
            lsr.invoke(args);
        }
    }


}
